﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using DomainDriver.CommonUtilities.Reflectors;
using DomainDriver.DomainModeling.Constraints;
using DomainDriver.DomainModeling.DataProviders;
using DomainDriver.DomainModeling.DomainModels;
using DomainDriver.DomainModeling.DomainModels.ConstructionResources;
using DomainDriver.DomainModeling.DomainObjects;
using DomainDriver.DomainModeling.StorageManagers;
using DomainDriver.DomainModeling.StorageManagers.DataSetStorage;
using DomainDriver.DomainModeling.StorageManagers.EntityFrameworkStorage;
using DomainDriver.DomainModeling.Repositories;
using AutomotivePricingModel.BodyStyles;
using AutomotivePricingModel.Brands;
using AutomotivePricingModel.Companies;
using AutomotivePricingModel.Countries;
using AutomotivePricingModel.Dealers;
using AutomotivePricingModel.DriveTrainTypes;
using AutomotivePricingModel.ProductionModels;
using AutomotivePricingModel.TransmissionTypes;
using AutomotivePricingModel.Vehicles;

namespace AutomotivePricingModel
{
    public class AutomotivePricingDomainModel : DomainModel, IBatchPersistableDomainModel<AutomotivePricingDataSet>
    {
        #region Constructors

        public AutomotivePricingDomainModel(DataSourcePersistenceType persistenceType, bool buildModelWithConstraints)
            : this(persistenceType, InvalidCallAction.Ignore, buildModelWithConstraints)
        { }

        public AutomotivePricingDomainModel(DataSourcePersistenceType persistenceType, InvalidCallAction invalidCallAction, bool buildModelWithConstraints)
            : base(persistenceType, invalidCallAction, buildModelWithConstraints)
        {
            RegisterDataSource<AutomotivePricingDataSet>(DataSourcePersistenceType.File, RepositoryConstructionMethods.CreateDataSetBackedRepository);
            RegisterDataSource<AutomotivePricingDatabaseContext>(DataSourcePersistenceType.Database, RepositoryConstructionMethods.CreateEntityFrameworkBackedRepository);

            RegisterDomainObjectRepository<BodyStyleEnum, int, BodyStyle>();
            RegisterDomainObjectRepository<Guid, Brand>();
            RegisterDomainObjectRepository<Guid, Company>();
            RegisterDomainObjectRepository<Guid, Country>();
            RegisterDomainObjectRepository<Guid, Dealer>();
            RegisterDomainObjectRepository<DriveTrainTypeEnum, int, DriveTrainType>();
            RegisterDomainObjectRepository<Guid, ProductionModel>();
            RegisterDomainObjectRepository<TransmissionTypeEnum, int, TransmissionType>();
            RegisterDomainObjectRepository<Guid, Vehicle>();

            CreateConstraints += ConstraintCreationHandler;

            Initialize();
        }

        public void ConstraintCreationHandler(object sender, EventArgs args)
        {
            // F(BodyStyle)
            FixedValuesConstraint<BodyStyleEnum, BodyStyle> bodyStyleEnumConstraint = new FixedValuesConstraint<BodyStyleEnum, BodyStyle>(this, AutomotivePricingFactory.LoadBodyStyleValues);
            AddModelConstraint(bodyStyleEnumConstraint);
            // F(DriveTrainType)
            FixedValuesConstraint<DriveTrainTypeEnum, DriveTrainType> driveTrainTypeEnumConstraint = new FixedValuesConstraint<DriveTrainTypeEnum, DriveTrainType>(this, AutomotivePricingFactory.LoadDriveTrainTypeValues);
            AddModelConstraint(driveTrainTypeEnumConstraint);
            // F(TransmissionType)
            FixedValuesConstraint<TransmissionTypeEnum, TransmissionType> transmissionTypeEnumConstraint = new FixedValuesConstraint<TransmissionTypeEnum, TransmissionType>(this, AutomotivePricingFactory.LoadTransmissionTypeValues);
            AddModelConstraint(transmissionTypeEnumConstraint);

            // O(Dealer)->E(Brand)
            ListRelationConstraint<Guid, Guid, Dealer, Brand> dealerBrandsListConstraint = new ListRelationConstraint<Guid, Guid, Dealer, Brand>(this, (Dealer dObj) => dObj.BrandsRepresented);
            AddModelConstraint(dealerBrandsListConstraint);
            // O(Dealer)->E(Vehicle)
            ListRelationConstraint<Guid, Guid, Dealer, Vehicle> dealerVehiclesListConstraint = new ListRelationConstraint<Guid, Guid, Dealer, Vehicle>(this, (Dealer dObj) => dObj.VehiclesInStock);
            AddModelConstraint(dealerVehiclesListConstraint);

            // P(Company)->C(Brand)
            ParentChildConstraint<Guid, Guid, Company, Brand> brandCompanyParentConstraint = new ParentChildConstraint<Guid, Guid, Company, Brand>(this, (Brand dObj) => (Guid)dObj.OwningCompany);
            AddModelConstraint(brandCompanyParentConstraint);
            // P(Country)->C(Brand)
            ParentChildConstraint<Guid, Guid, Country, Brand> brandCountryParentConstraint = new ParentChildConstraint<Guid, Guid, Country, Brand>(this, (Brand dObj) => (Guid)dObj.CountryOfOrigin);
            AddModelConstraint(brandCountryParentConstraint);
            // P(Country)->C(Company)
            ParentChildConstraint<Guid, Guid, Country, Company> companyCountryParentConstraint = new ParentChildConstraint<Guid, Guid, Country, Company>(this, (Company dObj) => (Guid)dObj.CountryOfHeadquarters);
            AddModelConstraint(companyCountryParentConstraint);
            // P(Brand)->C(ProductionModel)
            ParentChildConstraint<Guid, Guid, Brand, ProductionModel> productionModelBrandParentConstraint = new ParentChildConstraint<Guid, Guid, Brand, ProductionModel>(this, (ProductionModel dObj) => (Guid)dObj.ParentBrand);
            AddModelConstraint(productionModelBrandParentConstraint);
            // P(ProductionModel)->C(Vehicle)
            ParentChildConstraint<Guid, Guid, ProductionModel, Vehicle> vehicleProductionModelParentConstraint = new ParentChildConstraint<Guid, Guid, ProductionModel, Vehicle>(this, (Vehicle dObj) => (Guid)dObj.ParentProductionModel);
            AddModelConstraint(vehicleProductionModelParentConstraint);
            // P(BodyStyle)->C(VehicleConfig)
            ParentChildConstraint<BodyStyleEnum, Guid, BodyStyle, Vehicle> vehicleBodyStyleParentConstraint = new ParentChildConstraint<BodyStyleEnum, Guid, BodyStyle, Vehicle>(this, (Vehicle dObj) => (BodyStyleEnum)dObj.OptionsConfiguration.BodyStyle);
            AddModelConstraint(vehicleBodyStyleParentConstraint);
            // P(DriveTrain)->C(VehicleConfig)
            ParentChildConstraint<DriveTrainTypeEnum, Guid, DriveTrainType, Vehicle> vehicleDriveTrainTypeParentConstraint = new ParentChildConstraint<DriveTrainTypeEnum, Guid, DriveTrainType, Vehicle>(this, (Vehicle dObj) => (DriveTrainTypeEnum)dObj.OptionsConfiguration.DriveTrainType);
            AddModelConstraint(vehicleDriveTrainTypeParentConstraint);
            // P(TransmissionType)->C(VehicleConfig)
            ParentChildConstraint<TransmissionTypeEnum, Guid, TransmissionType, Vehicle> vehicleTransmissionTypeParentConstraint = new ParentChildConstraint<TransmissionTypeEnum, Guid, TransmissionType, Vehicle>(this, (Vehicle dObj) => (TransmissionTypeEnum)dObj.OptionsConfiguration.TransmissionType);
            AddModelConstraint(vehicleTransmissionTypeParentConstraint);
        }

        #endregion

        public void LoadFromDataSource(AutomotivePricingDataSet dataSource)
        {
            object dataSourceAsObject = dataSource;
            LoadFromDataSource(dataSourceAsObject);
        }

        public void PersistToDataSource(AutomotivePricingDataSet dataSource)
        {
            object dataSourceAsObject = dataSource;
            PersistToDataSource(dataSourceAsObject);
        }
    }
}